package com.jerry.shop.service.impl;

import com.jerry.shop.service.RedisLockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
 * redis 处理开锁、解锁
 */
@Component
@Slf4j
public class RedisLockServiceImpl implements RedisLockService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    //使用redis的setnx命令来存储, 若存在，则返回0, 若不存在，则返回1
    public boolean saveToRedisUsingSetnx(String key, String value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    //使用redis的get命令来获取值
    public String getFromRedis(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    //使用redis的getset命令来获取值
    public String getFromRedisUsingGetset(String key, String value) {
        return redisTemplate.opsForValue().getAndSet(key , value);
    }

    //使用redis的delete命令来删除值
    public void deleteFromRedis(String key) {
        redisTemplate.opsForValue().getOperations().delete(key);
    }

    /**
     * 加锁
     * @param key
     * @param value   value=当前时间 + 过期时间
     * @return
     */
    public boolean lock(String key , String value) {
        //判断redis中是否已经存在key, 若不存在，则说明没有加锁
        if(saveToRedisUsingSetnx(key, value)) {
            //返回true,代表加锁成功
            return true;
        }

        //从redis中获取存储的时间
        String redisSaveTime = getFromRedis(key);
        //当存储的时间 < 当前时间，说明存储的时间已经过期
        if(!StringUtils.isEmpty(redisSaveTime) && Long.parseLong(redisSaveTime) < System.currentTimeMillis()) {
            //多个线程同时访问时，第一个线程首先获取存储的时间，然后设置新的时间。下一个线程来时，获取的就是上一个线程设置的时间
            String oldSaveTime = getFromRedisUsingGetset(key,value);
            if(!StringUtils.isEmpty(oldSaveTime) && oldSaveTime.equals(redisSaveTime)) {
                //返回true,代表加锁成功
                return true;
            }
        }

        //返回false,代表加锁失败
        return false;
    }

    /**
     * 解锁
     * @param key
     * @param value
     */
    public void unlock(String key , String value) {
        try {
            //从redis中获取存储的时间
            String redisSaveTime = getFromRedis(key);
            if(!StringUtils.isEmpty(redisSaveTime) && redisSaveTime.equals(value)) {
                deleteFromRedis(key);
            }
        }catch (Exception e) {
            log.error("【redis分布式锁】解锁失败，{}" , e);
        }
    }

}
